package com.cjb.cloudfabric.service.impl;

import com.cjb.cloudfabric.config.FabricConfig;
import com.cjb.cloudfabric.entity.BasicAsset;
import com.cjb.cloudfabric.entity.ContractValue;
import com.cjb.cloudfabric.entity.DeviceStatus;
import com.cjb.cloudfabric.entity.FabricUser;
import com.cjb.cloudfabric.service.OperateFabricService;
import com.cjb.common.util.Common;
import com.cjb.common.util.JsonUtils;
import org.hyperledger.fabric.gateway.*;
import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
import org.hyperledger.fabric_ca.sdk.HFCAClient;
import org.hyperledger.fabric_ca.sdk.RegistrationRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.nio.file.Paths;
import java.security.PrivateKey;
import java.util.Properties;
import java.util.Set;

/**
 * @Author chenjinbin
 * @Package IntelliJ IDEA
 * @Class OperateFabric
 * @Date 2023-10-08 下午9:14
 */
@Service
public class OperateFabricServiceImpl implements OperateFabricService {
    /**
     * 日志
     */
    private final static Logger log = LoggerFactory.getLogger(OperateFabricServiceImpl.class);
    /**
     * 注入初始化合约
     */
    @Autowired
    private  FabricConfig fabricConfig;
    /**
     * 组织1证书
     */
    @Value("${fabric.config-path.org1cert}")
    private String org1Cert;
    /**
     * 钱包快捷路径
     */
    @Value("${fabric.basic-wallet}")
    private String walletPath;

    /*初始化资产*/
    public void InitLedger() throws Exception {
        Contract contract = fabricConfig.initContract(null);
        contract.submitTransaction("InitLedger");
        log.info("Submit Transaction: CreateAsset asset13");
    }

    /*这个智能合约有问题 没写*/
    @Override
    public String QueryAllAsset() throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.evaluateTransaction("GetAllAssets");

        log.info("Evaluate Transaction: GetAssetsByRange, result: " + new String(result));

        return new String(result);
    }

    /**
     * 查询全部的 链上设备数据
     *
     * @Author chenjinbin
     * @Value
     * @Title
     */
    @Override
    public String QueryAllDeviceData() throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.evaluateTransaction("GetAllDeviceData");

        log.info("Evaluate Transaction: GetAssetsByRange, result: " + new String(result));

        return new String(result);
    }
    /*注 觉得这个设计还是有瑕疵*/
//    private BasicAsset basicAsset;
    /**
     *
     * 查看全部资产列表
     * @Author chenjinbin
     * @Value
     * @Title
     */
    @Override
    public String SubmitAllAsset(BasicAsset basicAsset) throws Exception {
        byte[] result;
        log.info("进行初始化");
        Contract contract = fabricConfig.initContract(null);
        /*参数应该是动态的 所以我们这边使用动态String去接受 所以应该是
         * */
        log.info("进行提交");
        log.info("查看数据情况 {}", JsonUtils.toJsonString(basicAsset));
        result = contract.submitTransaction("CreateAsset", basicAsset.getId(), basicAsset.getName(), basicAsset.getAppraisedValue().toString(), basicAsset.getCollections().toString(), basicAsset.getOwner(), basicAsset.getId());
        //result = contract.submitTransaction("CreateAsset", "acc", "wewq", "32", "231", "uqwehuqw", "32");
        //return new String(contract.submitTransaction("CreateAsset", "asset14", "yellow", "5", "Tom", "1300"));
        log.info("提交成功");
        return new String(result);
    }
    /**
     * 硬件设备 数据上链接口补充
     *
     * @Author chenjinbin
     * @Value
     * @Title
     */
    @Override
    public String SubmitDeviceData(DeviceStatus deviceStatus) throws Exception {
        byte[] result = new byte[1024];
        log.info("进行初始化");
        Contract contract = fabricConfig.initContract(null);
        /*参数应该是动态的 所以我们这边使用动态String去接受 所以应该是
         * */
        log.info("进行提交");
        log.info("查看数据情况 {}", JsonUtils.toJsonString(deviceStatus));
        if (Common.isNotEmptyVales(deviceStatus,deviceStatus.getDeviceId(),deviceStatus.getDeviceTemperature(), deviceStatus.getDeviceHumidity(),deviceStatus.getDeviceBrightness(),deviceStatus.getDeviceStatus(),deviceStatus.getTemperatureStatus(),deviceStatus.getBrightnessStatus())){
            result = contract.submitTransaction("CreateDeviceData", deviceStatus.getDeviceId(), deviceStatus.getDeviceTemperature(), deviceStatus.getDeviceHumidity().toString(), deviceStatus.getDeviceBrightness(), deviceStatus.getDeviceStatus().toString(), deviceStatus.getTemperatureStatus().toString(),deviceStatus.getBrightnessStatus().toString());
        }
        else {
            log.error("上报数据缺失");
        }
        //result = contract.submitTransaction("CreateAsset", "acc", "wewq", "32", "231", "uqwehuqw", "32");
        //return new String(contract.submitTransaction("CreateAsset", "asset14", "yellow", "5", "Tom", "1300"));
        log.info("提交成功");
        return new String(result);
    }


    /*通过assetid获取资产 这个很重要*/
    @Override
    public String QueryAssetById(String assetId) throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(new ContractValue());
        result = contract.evaluateTransaction("ReadAsset", assetId);
        log.info("result: " + new String(result));
        return new String(result);
    }

    /*判断是否存在
     * */
    @Override
    public String KnowExit(String assetId) throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.evaluateTransaction("AssetExists", assetId);
        log.info("result: " + new String(result));
        return new String(result);
    }

    /*删除资产*/
    @Override
    public String DeleteAsset(String assetId) throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(new ContractValue());
        /*注意这边可以删除多个
         * */
        result = contract.submitTransaction("DeleteAsset", assetId);
        return new String(result);
    }

    /*资产交易*/
    /*转让 Tomoko > owner Tom
     * */
    @Override
    public String TransferAsset(String assetId, String buyer) throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.submitTransaction("TransferAsset", assetId, buyer);
        return new String(result);
    }

    /*富查询
     *  Rich Query with Pagination (Only supported if CouchDB is used as state database
     *  */
    @Override
    public void RichQuery() throws Exception {
//        result = contract.evaluateTransaction("QueryAssetsWithPagination","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}","3","");
        /*看了下 这个先不用好了*/
    }

    /*部分资产转移 感觉这个用的会比较多
     * */
    @Override
    public void PartAsset(String parm, String owner) throws Exception {
        Contract contract = fabricConfig.initContract(null);
        log.info("Submit Transaction: TransferAssetByColor yellow assets > newOwner Michel");
        contract.submitTransaction("TransferAssetByColor", parm, owner);
        log.info("成功");
    }

    /*还是部分用户的富查询
     * */
    @Override
    public void QueryAssetsByOwner(String owner) throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.evaluateTransaction("QueryAssetsByOwner", owner);
        log.info("result: " + new String(result));
    }

    /*历史数据获取
     * */
    @Override
    public String GetHistoryData(String assetId) throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.evaluateTransaction("QueryAssetHistory", assetId);
        log.info("result: " + new String(result));
        return new String(result);
    }

    /*
     * 分页查询
     * */
    @Override
    public void PagingQuery() throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.evaluateTransaction("QueryAssets", "{\"selector\":{\"size\":15}}");
        log.info("result: " + new String(result));
    }

    /*富查询的某一种*/
    @Override
    public void RichQueryOne() throws Exception {
//        log.info("\n");
//            log.info("Evaluate Transaction:QueryAssets Jin Soo's assets");
//            result = contract.evaluateTransaction("QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Jin Soo\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}");
//            log.info("result: " + new String(result));
    }

    /*一定范围中的查询
     * */
    @Override
    public void QueryPaging() throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        log.info("Evaluate Transaction:GetAssetsByRangeWithPagination assets 3-5");
        result = contract.evaluateTransaction("GetAssetsByRangeWithPagination", "asset3", "asset6", "3", "");
        log.info("result: " + new String(result));
    }

    /*owner查询*/
    @Override
    public String QueryAssetByOwner(String id) throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.evaluateTransaction("ReadAsset", id);
        log.info("result: " + new String(result));
        return new String(result);
    }

    //    查询String
    @Override
    public String getQueryResultForQueryString(String owner) throws Exception {
        byte[] result;
        Contract contract = fabricConfig.initContract(null);
        result = contract.evaluateTransaction("QueryAssetByOwner", "public", owner);
        log.info("result: " + new String(result));
        return new String(result);
    }

    /**
     * 服务证书生成
     *
     * @Author chenjinbin
     * @Value
     * @Title
     */
    @Override
    public String becomeCertificate(FabricUser fabricUser) throws Exception{
        // Create a CA client for interacting with the CA.
        Properties props = new Properties();
        props.put("pemFile",
                org1Cert);
        props.put("allowAllHostNames", "true");
        HFCAClient caClient = HFCAClient.createNewInstance("https://localhost:7054", props);
        CryptoSuite cryptoSuite = CryptoSuiteFactory.getDefault().getCryptoSuite();
        caClient.setCryptoSuite(cryptoSuite);

        // Create a wallet for managing identities
        Wallet wallet = Wallets.newFileSystemWallet(Paths.get(walletPath));

        // Check to see if we've already enrolled the user.
        if (wallet.get(fabricUser.getUserName()) != null) {
            log.info("An identity for the user \""+fabricUser.getUserName()+"\" already exists in the wallet");
            return "";
        }

        X509Identity adminIdentity = (X509Identity) wallet.get("admin");
        if (adminIdentity == null) {
            log.info("\"admin\" needs to be enrolled and added to the wallet first");
            return "";
        }
        User admin = new User() {
            @Override
            public String getName() {
                return "admin";
            }

            @Override
            public Set<String> getRoles() {
                return fabricUser.getRoles();
            }

            @Override
            public String getAccount() {
                return fabricUser.getAccount();
            }

            @Override
            public String getAffiliation() {
                return "org1.department1";
            }

            @Override
            public Enrollment getEnrollment() {
                return new Enrollment() {
                    @Override
                    public PrivateKey getKey() {
                        return adminIdentity.getPrivateKey();
                    }

                    @Override
                    public String getCert() {
                        return Identities.toPemString(adminIdentity.getCertificate());
                    }
                };
            }

            @Override
            public String getMspId() {
                return "Org1MSP";
            }

        };
        if (fabricUser.getUserName() == null) {
            // Register the user, enroll the user, and import the new identity into the wallet.
            RegistrationRequest registrationRequest = new RegistrationRequest("appUser");
            registrationRequest.setAffiliation("org1.department1");
            registrationRequest.setEnrollmentID("appUser");
            String enrollmentSecret = caClient.register(registrationRequest, admin);
            Enrollment enrollment = caClient.enroll("appUser", enrollmentSecret);
            Identity user = Identities.newX509Identity("Org1MSP", enrollment);
            wallet.put("appUser", user);
            log.info("Successfully enrolled user \"appUser\" and imported it into the wallet");
            return walletPath+"/"+"appUser.id";
        } else {
            RegistrationRequest registrationRequest = new RegistrationRequest(fabricUser.getUserName());
            registrationRequest.setAffiliation("org1.department1");
            registrationRequest.setEnrollmentID(fabricUser.getUserName());
            String enrollmentSecret = caClient.register(registrationRequest, admin);
            Enrollment enrollment = caClient.enroll(fabricUser.getUserName(), enrollmentSecret);
            Identity user = Identities.newX509Identity("Org1MSP", enrollment);
            wallet.put(fabricUser.getUserName(), user);
            log.info("Successfully enrolled user \"appUser\" and imported it into the wallet");
            return walletPath+"/"+fabricUser.getUserName()+".id";
        }
    }
}
